<template>
  <div class="editable-tree">
    <ElButton
      v-if="!readonly && addRootNodeButtonStyle === 'top-right'"
      type="primary"
      size="mini"
      style="position: absolute; top: 4px; z-index: 1"
      :style="{ right: `${right}px` }"
      icon="el-icon-plus"
      circle
      plain
      @click.stop="$emit('add-root-node')" />
    <div ref="treeWrapper" class="et-tree-wrapper">
      <div v-if="!readonly && addRootNodeButtonStyle === 'top'" style="padding-left: 24px">
        <ElButton :disabled="loading" size="mini" type="text" @click="appendRootNode">
          <i class="el-icon-lx-add"></i> {{ addRootNodeLabel ? addRootNodeLabel : `添加${label}` }}
        </ElButton>
      </div>
      <ElTree
        v-bind="filterTreeProperties($attrs)"
        :key="treeKey"
        ref="tree"
        class="flex-item"
        :default-expanded-keys="expandedKeys"
        :expand-on-click-node="false"
        highlight-current
        :load="loadNode"
        :data="innerTreeData"
        :props="treeProps"
        :lazy="lazy"
        :node-key="nodeKey"
        v-on="filterTreeEvents($listeners)"
        @node-click="nodeClick"
        @node-contextmenu="showContextMenu"
        @node-expand="addExpandKey"
        @node-collapse="removeExpandKey">
        <template #default="{ node, data }">
          <slot :node="node" :data="data">
            <!--非编辑模式-->
            <template v-if="!data.edit">
              <span :data-id="data.id" class="custom-tree-node">
                <span class="node-label flex-row flex-item ellipsis-1" style="overflow: hidden">
                  <i v-if="icon" :class="data.loading ? 'el-icon-loading' : icon(data, node)" class="node-icon"></i>
                  <span class="flex-item-relative" style="height: 21px">
                    <span class="ellipsis-1 absolute-box et-tree-label" :title="node.label">
                      <slot name="label" :data="data" :node="node">{{ node.label }}</slot>
                    </span>
                  </span>
                  <slot name="label-suffix" :node="node" :data="data"></slot>
                </span>
                <span v-if="!contextMenu && !readonly" v-show="!data.loading && !loading" class="handle-actions">
                  <ElButton size="mini" type="text" @click.stop="appendChildNode(node, data)">添加</ElButton>
                  <ElButton size="mini" type="text" @click.stop="editNode(node, data)">修改</ElButton>
                  <ElPopconfirm class="pop-confirm" icon-color="red" title="你确定要删除吗？" @confirm="removeNode(node, data)">
                    <template #reference>
                      <ElButton v-show="node.isLeaf" size="mini" style="margin-left: 4px" type="text" @click.stop>删除</ElButton>
                    </template>
                  </ElPopconfirm>
                </span>
                <span
                  v-if="contextMenuIcon && contextMenu && !readonly"
                  v-show="!data.loading && !loading"
                  class="handle-actions"
                  style="display: inline-block">
                  <ElButton size="mini" style="padding: 6px 10px 0" type="text" @click.stop="showContextMenu($event, data, node)">. . .</ElButton>
                </span>
              </span>
            </template>
            <!--编辑模式-->
            <template v-else>
              <span :data-id="data.id" class="custom-tree-node flex-row flex-item" style="overflow: hidden">
                <i v-if="icon" :class="data.loading ? 'el-icon-loading' : 'el-icon-edit-outline'" class="node-icon flex-not-shrink"></i>
                <ElInput
                  ref="labelTxt"
                  v-model="text"
                  class="node-input flex-item"
                  size="mini"
                  :placeholder="defaultOptions.inputPlaceholder"
                  @keyup.native="event => inputKeyup(event, node, data)"
                  @blur="event => inputBlur(event, node, data)"
                  @click.native.stop />
                <template v-if="confirmButton">
                  <ElButton v-show="!data.loading" class="flex-not-shrink" size="mini" type="text" @click.stop="onConfirm(node, data)">确定</ElButton>
                  <ElButton v-show="!data.loading" class="flex-not-shrink" size="mini" type="text" @click.stop="onCancel(node, data)">取消</ElButton>
                </template>
              </span>
            </template>
          </slot>
        </template>
      </ElTree>
      <div v-show="showMenu" v-if="contextMenu && !readonly" ref="contextMenu" :style="position" class="menu">
        <ul class="menu-list">
          <slot name="context-menu" :cur-node="curNode" :cur-data="curData">
            <li v-show="curData.id > -1" class="menu-item" @click="appendPeerNode(curNode, curData)">
              <i class="menu-item-icon el-icon-lx-add" style="color: #228b22"></i>{{ menuLabel.appendPeer }}
            </li>
            <li class="menu-item" @click="appendChildNode(curNode, curData)">
              <i class="menu-item-icon el-icon-lx-add" style="color: #228b22"></i>{{ menuLabel.appendChild }}
            </li>
            <li v-show="curData.id > -1" class="menu-item" @click="editNode(curNode, curData)">
              <i class="menu-item-icon el-icon-edit-outline" style="color: #ffa500"></i>{{ menuLabel.editNode }}
            </li>
            <li v-show="curNode.isLeaf" class="menu-item" @click="removeNodeModal(curNode, curData)">
              <i class="menu-item-icon el-icon-delete" style="color: #f00"></i>{{ menuLabel.removeNode }}
            </li>
          </slot>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
  import {
    isUndefined,
    hasOwn,
    computePosition,
    delay,
    isNotEmpty,
    isVerticalScrollbarVisible,
    scrollbarWidth,
    findTreeChildren,
    fastClone,
    isPlainObject,
    isFunction
  } from '@web-utils/core'
  import { addResizeListener, removeResizeListener } from '@web-utils/integrations/resize'
  import { cloneDeep } from '@web-utils/integrations/lodash'
  import { assignReactiveProperties } from '@/util/reactive'
  import { filterTreeEvents, filterTreeProperties } from '@/util'

  export default {
    name: 'EditableTree',
    props: {
      label: {
        type: String,
        default: '书签'
      },
      addRootNodeLabel: String,
      defaultExpandedKeys: {
        type: Array,
        default: () => []
      },
      newNodeBuilder: {
        type: Function,
        default: (name, parent) => {
          return {
            name,
            parentId: parent.id
          }
        }
      },
      contextMenu: Boolean,
      lazy: {
        type: Boolean,
        default: true
      },
      expandFirstNode: {
        type: Boolean,
        default: true
      },
      readonly: Boolean,
      saveApi: Function,
      updateApi: Function,
      deleteApi: Function,
      fetchApi: Function,
      customRootNode: [Object, Function],
      rootNode: Object,
      contextMenuIcon: Boolean,
      contextMenuInline: Boolean,
      treeProps: {
        type: Object,
        default: () => {
          return {
            children: 'children',
            label: 'name',
            isLeaf: data => {
              if (hasOwn(data, 'hasChildren')) {
                return !data.hasChildren
              } else if (hasOwn(data, 'hasChild')) {
                return !data.hasChild
              } else if (hasOwn(data, 'children') && data.children) {
                return data.children.length === 0
              } else {
                return true
              }
            }
          }
        }
      },
      menuLabel: {
        type: Object,
        default: () => ({
          appendPeer: '添加同级节点',
          appendChild: '添加',
          removeNode: '删除',
          editNode: '修改'
        })
      },
      icon: {
        type: [Function, Boolean],
        default: (data, node) => {
          return 'el-icon-collection-tag'
        }
      },
      confirmButton: {
        type: Boolean,
        default: true
      },
      addRootNodeButtonStyle: {
        type: String,
        default: 'top'
      },
      reserveExpandKey: Boolean,
      nodeKey: {
        type: String,
        default: 'id'
      }
    },
    data() {
      return {
        loading: false,
        showMenu: false,
        empty: true,
        expandedKeys: this.lazy ? [] : this.defaultExpandedKeys,
        treeData: [],
        curNode: {},
        curData: {},
        position: {},
        text: '',
        right: 4,
        treeKey: Date.now()
      }
    },
    computed: {
      defaultOptions() {
        return {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          closeOnClickModal: false,
          modal: true,
          inputErrorMessage: `${this.label}名不能为空！`,
          inputPlaceholder: `请输入${this.label}名...`,
          inputValidator: value => value != null && value.trim().length > 0
        }
      },
      innerTreeData() {
        return this.$attrs.data?.length ? this.$attrs.data : this.treeData
      }
    },
    watch: {
      defaultExpandedKeys: {
        immediate: true,
        handler(newVal) {
          this.expandedKeys = [...newVal]
        }
      }
    },
    created() {
      this.tryFetch()
    },
    mounted() {
      addResizeListener(this.$refs.tree.$el, this.resize)
      addResizeListener(this.$refs.treeWrapper, this.resize)
    },
    beforeDestroy() {
      removeResizeListener(this.$refs.tree.$el, this.resize)
      removeResizeListener(this.$refs.treeWrapper, this.resize)
    },
    methods: {
      filterTreeProperties,
      filterTreeEvents,
      addExpandKey(data) {
        if (this.reserveExpandKey) {
          this.expandedKeys.push(data[this.nodeKey])
        }
      },
      removeExpandKey(data) {
        if (this.reserveExpandKey) {
          this.expandedKeys = this.expandedKeys.filter(key => key !== data[this.nodeKey])
        }
      },
      resize() {
        if (isVerticalScrollbarVisible(this.$refs.treeWrapper)) {
          this.right = 4 + scrollbarWidth
        } else {
          this.right = 4
        }
      },
      async showContextMenu(event, data, node) {
        if (data.contextmenu === false || data.id === -1 || data.id === 0 || data.edit === true) {
          return
        }
        if (this.contextMenu) {
          this.curNode = node
          this.curData = data
          this.showMenu = true
          this.$refs.tree.setCurrentKey(data.id)
          await this.$nextTick()
          this.position = computePosition(event, this.$refs.contextMenu)
          window.addEventListener('click', this.hideContextMenu)
        }
      },
      hideContextMenu() {
        this.showMenu = false
        window.removeEventListener('click', this.hideContextMenu)
      },
      async tryFetch() {
        if (this.lazy === false && this.fetchApi) {
          let res
          try {
            res = await this.fetchApi(this.rootNode?.id ?? -1, {})
          } catch (e) {
            return
          }
          let result = res.data.data
          // 兼容返回对象的情况
          if (isPlainObject(result)) {
            result = [result]
          }
          result = result || []
          let treeData = []
          this.empty = result.length === 0
          if (isPlainObject(this.customRootNode)) {
            const data = cloneDeep(this.customRootNode)
            data[this.treeProps.children || 'children'] = result
            treeData = [data]
          } else if (isFunction(this.customRootNode)) {
            const data = this.customRootNode(result)
            data[this.treeProps.children || 'children'] = result
            treeData = isPlainObject(data) ? [data] : data
          } else {
            treeData = result
          }
          if (this.defaultExpandedKeys?.length) {
            this.expandedKeys = [...this.defaultExpandedKeys]
          } else {
            if (this.expandFirstNode) {
              this.expandedKeys = [treeData[0]?.id]
            }
          }
          this.treeData = treeData
        }
      },
      async loadNode(node, resolve) {
        if (node.level === 0) {
          const res = await this.fetchApi(this.rootNode?.id ?? -1, node)
          let result = res.data.data
          this.empty = result.length === 0
          if (isPlainObject(this.customRootNode)) {
            const data = cloneDeep(this.customRootNode)
            data[this.treeProps.children || 'children'] = result
            result = [data]
            resolve(result)
          } else if (isFunction(this.customRootNode)) {
            const data = this.customRootNode(result)
            data[this.treeProps.children || 'children'] = result
            result = isPlainObject(data) ? [data] : data
          } else {
            resolve(result)
          }
          await this.$nextTick()
          this.$emit('root-node-loaded', result)
          if (this.defaultExpandedKeys?.length) {
            this.expandedKeys = [result[0]?.id, ...this.defaultExpandedKeys]
          } else {
            if (this.expandFirstNode) {
              this.expandedKeys = [result[0]?.id]
            }
          }
        } else {
          if (node.leaf || node.data.isTempNode) {
            return resolve([])
          }
          const res = await this.fetchApi(node.data.id, node)
          const result = res.data.data
          resolve(result)
          await this.$nextTick()
          this.$emit('node-loaded', result)
        }
      },
      clear() {
        this.cancelEdit()
        this.removeNewNode()
      },
      clearChecked() {
        this.$refs.tree.setCheckedKeys([])
      },
      removeNewNode() {
        if (this.newChild && this.newChild.id === -1) {
          this.$refs.tree.remove(this.newChild)
          this.$emit('remove-new-node')
        }
      },
      cancelEdit() {
        if (this.editData) {
          if (isUndefined(this.editData.edit)) {
            this.$set(this.editData, 'edit', true)
          } else {
            this.editData.edit = false
          }
        }
      },
      async editNode(node, data) {
        // 行内编辑模式
        if (!this.contextMenu || this.contextMenuIcon || this.contextMenuInline) {
          this.clear()
          this.editData = data
          this.text = data[this.treeProps.label]
          if (isUndefined(data.edit)) {
            this.$set(data, 'edit', true)
          } else {
            data.edit = true
          }
          await this.$nextTick()
          this.$refs.labelTxt.focus()
        } else {
          await this.$prompt(`请输入新的${this.label}名：`, {
            ...this.defaultOptions,
            inputValue: data[this.treeProps.label],
            beforeClose: async (action, instance, done) => {
              if (action === 'confirm') {
                try {
                  instance.confirmButtonLoading = true
                  instance.confirmButtonText = '处理中...'
                  this.text = instance.inputValue
                  await this.onConfirm(this.curNode, this.curData)
                  done()
                } catch (e) {
                  instance.confirmButtonText = '确定'
                }
                instance.confirmButtonLoading = false
              } else {
                done()
              }
            }
          }).catch(_ => _)
        }
      },
      async appendRootNode() {
        this.text = ''
        // 行内编辑模式
        if (!this.contextMenu || this.contextMenuIcon || this.contextMenuInline) {
          this.clear()
          this.createNewChild(undefined)
          this.$refs.tree.root.insertChild({ data: this.newChild }, undefined, true)
          await this.scrollIntoView(true)
        } else {
          await this.$prompt(`请输入${this.label}名：`, {
            ...this.defaultOptions,
            beforeClose: async (action, instance, done) => {
              if (action === 'confirm') {
                try {
                  this.createNewChild(-1)
                  instance.confirmButtonLoading = true
                  instance.confirmButtonText = '处理中...'
                  const res = await this.saveApi(this.newNodeBuilder(instance.inputValue, { id: -1 }))
                  this.newChild[this.treeProps.label] = instance.inputValue
                  this.newChild.edit = false
                  assignReactiveProperties(this.newChild, res.data)
                  this.$refs.tree.root.insertChild({ data: this.newChild }, undefined, true)
                  await this.locateView()
                  done()
                } catch (e) {
                  instance.confirmButtonText = '确定'
                }
                instance.confirmButtonLoading = false
              } else {
                done()
              }
            }
          }).catch(_ => _)
        }
      },
      appendChildNode(node) {
        this.text = ''
        const expanded = node.expanded
        node.expand(async () => {
          // 行内编辑模式
          if (!this.contextMenu || this.contextMenuIcon || this.contextMenuInline) {
            this.clear()
            this.createNewChild(node.data.id)
            this.$refs.tree.append(this.newChild, node)
            await this.scrollIntoView(expanded)
          } else {
            await this.$prompt(`请输入${this.label}名：`, {
              ...this.defaultOptions,
              beforeClose: async (action, instance, done) => {
                if (action === 'confirm') {
                  try {
                    this.createNewChild(node.data.id)
                    instance.confirmButtonLoading = true
                    instance.confirmButtonText = '处理中...'
                    const res = await this.saveApi(this.newNodeBuilder(instance.inputValue, node.data))
                    this.newChild[this.treeProps.label] = instance.inputValue
                    this.newChild.edit = false
                    assignReactiveProperties(this.newChild, res.data)
                    this.$refs.tree.append(this.newChild, node)
                    await this.locateView()
                    done()
                  } catch (e) {
                    instance.confirmButtonText = '确定'
                  }
                  instance.confirmButtonLoading = false
                } else {
                  done()
                }
              }
            }).catch(_ => _)
          }
        })
      },
      appendChildNodeWithData(node, parentId, label) {
        this.text = ''
        const expanded = node.expanded
        node.expand(async () => {
          this.clear()
          this.newChild = {
            id: -1,
            parentId,
            [this.treeProps.label]: label,
            edit: false,
            isTempNode: true,
            hasChildren: false
          }
          this.$refs.tree.append(this.newChild, node)
          await this.scrollIntoView(expanded)
        })
      },
      appendPeerNode(node) {
        if (node.parent && node.parent.level !== 0) {
          this.appendChildNode(node.parent)
        } else {
          this.appendRootNode()
        }
      },
      async addRootNode(data) {
        this.$refs.tree.remove(data)
        await this.$nextTick()
        this.$refs.tree.root.insertChild({ data }, undefined, true)
      },
      async addChildNode(data) {
        if (this.curNode && this.curNode.level !== 0) {
          this.curNode.expand(async () => {
            this.$refs.tree.remove(data)
            await this.$nextTick()
            this.$refs.tree.append(data, this.curNode)
          })
        } else {
          await this.addRootNode(data)
        }
      },
      async addPeerNode(data) {
        if (this.curNode && this.curNode.parent && this.curNode.parent.level !== 0) {
          this.curNode.parent.expand(async () => {
            this.$refs.tree.remove(data)
            await this.$nextTick()
            this.$refs.tree.append(data, this.curNode.parent)
          })
        } else {
          await this.addRootNode(data)
        }
      },
      updateNode(data) {
        const node = this.$refs.tree.getNode(data)
        node && (node.data = data)
      },
      setCurrentKey(key) {
        this.$refs.tree && this.$refs.tree.setCurrentKey(key)
      },
      createNewChild(parentId) {
        this.newChild = {
          id: -1,
          parentId,
          [this.treeProps.label]: '',
          edit: true,
          isTempNode: true,
          hasChildren: false
        }
        this.$emit('create-new-node', this.newChild)
      },
      async removeNodeModal(node) {
        await this.$confirm(
          `你确定要删除${this.label}[<span class="break-word" style="color: orange">${node.data[this.treeProps.label]}</span>]吗？`,
          '系统提示',
          {
            type: 'warning',
            modal: true,
            dangerouslyUseHTMLString: true,
            iconClass: 'el-icon-question',
            closeOnClickModal: false,
            confirmButtonClass: 'el-button--danger',
            cancelButtonClass: 'el-button--primary is-plain',
            beforeClose: async (action, instance, done) => {
              if (action === 'confirm') {
                try {
                  instance.confirmButtonLoading = true
                  instance.confirmButtonText = '删除...'
                  const parentNode = node.parent
                  const res = await this.deleteApi(node.data.id)
                  if (res.code === 0) {
                    this.$message.success('删除成功!')
                    this.$refs.tree.remove(node)
                    this.$emit('node-deleted', parentNode, node.data)
                    done()
                  } else {
                    this.$message.error(res.message)
                  }
                } catch (_) {}
                instance.confirmButtonText = '确定'
                instance.confirmButtonLoading = false
              } else {
                done()
              }
            }
          }
        ).catch(_ => _)
      },
      async removeNode(node) {
        try {
          const parentNode = node.parent
          if (this.deleteApi) {
            const res = await this.deleteApi(node.data.id)
            if (res.code === 0) {
              this.$message.success('删除成功!')
              this.$refs.tree.remove(node)
              this.$emit('node-deleted', parentNode, node.data)
            } else {
              this.$message.error(res.message)
            }
          } else {
            this.$refs.tree.remove(node)
          }
        } catch (e) {}
      },
      async onConfirm(node, data) {
        if (data[this.treeProps.label] === this.text || !isNotEmpty(this.text)) {
          this.clear()
          return
        }

        if (isUndefined(data.loading)) {
          this.$set(data, 'loading', true)
        } else {
          data.loading = true
        }

        try {
          this.loading = true
          if (data.id === -1) {
            const res = await this.saveApi(this.newNodeBuilder(this.text, node.parent.data || { id: -1 }))
            const old = fastClone(data)
            assignReactiveProperties(data, { ...res.data, isTempNode: false })
            this.$emit('node-added', data, old, node)
            this.registerNode(node)
          } else {
            await this.updateApi({ ...data, [this.treeProps.label]: this.text })
          }
          data[this.treeProps.label] = this.text
          data.edit = false
          this.$emit('node-updated', data, node)
          this.text = ''
        } catch (e) {}
        data.loading = false
        this.loading = false
      },
      onCancel(node, data) {
        if (data.id === -1) {
          this.$refs.tree.remove(node)
        } else {
          data.edit = false
        }
        this.text = ''
      },
      nodeClick(data, node, tree) {
        this.hideContextMenu()
        if (isUndefined(data.loading) || !data.loading) {
          this.curNode = node
          this.curData = data
          this.$emit('on-node-click', data, node, tree)
        }
      },
      getCurrentNode() {
        return this.$refs.tree.getCurrentNode()
      },
      async locateView() {
        await this.$nextTick()
        const el = this.$el.querySelector(`[data-id="${this.newChild.id}"]`)
        if (el) {
          el.scrollIntoView()
        }
      },
      async scrollIntoView(expanded) {
        await this.$nextTick()
        if (!expanded) {
          await delay(300)
        }
        this.$refs?.labelTxt?.focus()
        /* setTimeout(() => {
          const input = this.$refs.tree.$el.querySelector('input[type="text"]')
          const treeNodeDom = findParent(input, '.el-tree-node')
          const scroller = findScrollParent(treeNodeDom.offsetParent)
          if (scroller && treeNodeDom.offsetTop > scroller.scrollTop) {
            scroller.scrollTop = treeNodeDom.offsetTop + treeNodeDom.clientHeight + 4 * 2 + 20
          }
        }, 300) // 延迟 300 ms 等待动画结束 */
      },
      inputKeyup(event, node, data) {
        if (event.keyCode === 13) {
          this.onConfirm(node, data)
        }
        if (event.keyCode === 27) {
          this.clear()
        }
      },
      inputBlur(event, node, data) {
        if (this.confirmButton) {
          return
        }
        this.onConfirm(node, data)
      },
      getCheckedKeys() {
        return this.$refs.tree.getCheckedKeys()
      },
      setCheckedKeys(keys) {
        this.$refs.tree.setCheckedKeys(keys)
      },
      getCheckedNodes() {
        return this.$refs.tree.getCheckedNodes()
      },
      setCheckedNodes(nodes) {
        this.$refs.tree.setCheckedNodes(nodes)
      },
      getNode(data) {
        return this.$refs.tree.getNode(data)
      },
      getRootNode() {
        return this.$refs.tree.root
      },
      findTreeNode(data) {
        return findTreeChildren(this.$refs.tree.root.childNodes, node => node.data === data || node.data.id === data, 'childNodes')
      },
      registerNode(node) {
        node.store.registerNode(node)
        delete node.store.nodesMap[-1]
      },
      async reloadTree() {
        if (this.lazy) {
          this.treeKey = Date.now()
        } else {
          await this.tryFetch()
        }
      }
    }
  }
</script>

<style lang="scss">
  .editable-tree {
    position: relative;

    // height: 100%;
    &.et-tree-label-overflow-visible {
      .et-tree-label {
        position: static;
      }
    }
  }

  .et-tree-wrapper {
    display: flex;
    flex-direction: column;
    width: 100%;

    // height: 100%;
    overflow: auto;
    text-align: left;

    .el-tree {
      display: table;
      width: 100%;
      overflow: visible;
    }

    .el-button + .el-button {
      margin-left: 8px !important;
    }

    .el-input__inner {
      padding: 0 4px;
    }

    .custom-tree-node {
      display: flex;
      align-items: center;
      width: 100%;
      padding-right: 8px;
      font-size: 14px;

      &:hover {
        .handle-actions {
          display: block;

          // margin-left: 10px;
        }
      }

      .handle-actions {
        display: none;
        align-self: flex-end;
      }

      .node-icon {
        margin-top: 2px;
        margin-right: 4px;
      }
    }

    .node-label {
      align-items: center;
    }

    .node-input {
      min-width: 140px;
      margin-right: 10px;
    }

    .menu {
      position: fixed;
      z-index: 3;
      padding: 0;
      background: #fff;
      border-radius: 4px;
      box-shadow: 0 5px 10px #bebebe;
    }

    .menu-item {
      display: block;
      padding: 8px 20px 8px 8px;
      color: #242f42;
      font:
        normal 11px tahoma,
        arial,
        sans-serif;
      line-height: 100%;
      white-space: nowrap;
      cursor: pointer;
      user-select: none;
    }

    .menu .menu-item:hover {
      color: #66b1ff;
      background-color: #ecf5ff;
    }

    .menu-item-icon {
      margin-right: 4px;
      font-weight: 700;
      font-size: 1.3em;
    }
  }
</style>
